/*
** $Id: lparser.c,v 2.155 2016/08/01 19:51:24 roberto Exp $
** Lua Parser
** See Copyright Notice in lua.h
*/

#define lparser_c
#define LUA_CORE

#include "lprefix.h"


#include <string.h>

#include "lua.h"

#include "lcode.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "llex.h"
#include "lmem.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lparser.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"



/* maximum number of local variables per function (must be smaller
   than 250, due to the bytecode format) */
#define MAXVARS     200


#define hasmultret(k)       ((k) == VCALL || (k) == VVARARG)


/* because all strings are unified by the scanner, the parser
   can use pointer equality for string equality */
#define eqstr(a,b)  ((a) == (b))


/*
** nodes for block list (list of active blocks)
*/
typedef struct BlockCnt {
    struct BlockCnt* previous;  /* chain */
    int firstlabel;  /* index of first label in this block */
    int firstgoto;  /* index of first pending goto in this block */
    lu_byte nactvar;  /* # active locals outside the block */
    lu_byte upval;  /* true if some variable in the block is an upvalue */
    lu_byte isloop;  /* true if 'block' is a loop */
} BlockCnt;



/*
** prototypes for recursive non-terminal functions
*/
static void statement( LexState* ls );
static void expr( LexState* ls, expdesc* v );


/* semantic error */
static l_noret semerror( LexState* ls, const char* msg ) {
    ls->t.token = 0;  /* remove "near <token>" from final message */
    luaX_syntaxerror( ls, msg );
}


static l_noret error_expected( LexState* ls, int token ) {
    luaX_syntaxerror( ls,
                      luaO_pushfstring( ls->L, "%s expected", luaX_token2str( ls, token ) ) );
}


static l_noret errorlimit( FuncState* fs, int limit, const char* what ) {
    lua_State* L = fs->ls->L;
    const char* msg;
    int line = fs->f->linedefined;
    const char* where = ( line == 0 )
                        ? "main function"
                        : luaO_pushfstring( L, "function at line %d", line );
    msg = luaO_pushfstring( L, "too many %s (limit is %d) in %s",
                            what, limit, where );
    luaX_syntaxerror( fs->ls, msg );
}


static void checklimit( FuncState* fs, int v, int l, const char* what ) {
    if ( v > l ) {
        errorlimit( fs, l, what );
    }
}


static int testnext( LexState* ls, int c ) {
    if ( ls->t.token == c ) {
        luaX_next( ls );
        return 1;
    }
    else {
        return 0;
    }
}


static void check( LexState* ls, int c ) {
    if ( ls->t.token != c ) {
        error_expected( ls, c );
    }
}


static void checknext( LexState* ls, int c ) {
    check( ls, c );
    luaX_next( ls );
}


#define check_condition(ls,c,msg)   { if (!(c)) luaX_syntaxerror(ls, msg); }



static void check_match( LexState* ls, int what, int who, int where ) {
    if ( !testnext( ls, what ) ) {
        if ( where == ls->linenumber ) {
            error_expected( ls, what );
        }
        else {
            luaX_syntaxerror( ls, luaO_pushfstring( ls->L,
                                                    "%s expected (to close %s at line %d)",
                                                    luaX_token2str( ls, what ), luaX_token2str( ls, who ), where ) );
        }
    }
}


static TString* str_checkname( LexState* ls ) {
    TString* ts;
    check( ls, TK_NAME );
    ts = ls->t.seminfo.ts;
    luaX_next( ls );
    return ts;
}


static void init_exp( expdesc* e, expkind k, int i ) {
    e->f = e->t = NO_JUMP;
    e->k = k;
    e->u.info = i;
}


static void codestring( LexState* ls, expdesc* e, TString* s ) {
    init_exp( e, VK, luaK_stringK( ls->fs, s ) );
}


static void checkname( LexState* ls, expdesc* e ) {
    codestring( ls, e, str_checkname( ls ) );
}


static int registerlocalvar( LexState* ls, TString* varname ) {
    FuncState* fs = ls->fs;
    Proto* f = fs->f;
    int oldsize = f->sizelocvars;
    luaM_growvector( ls->L, f->locvars, fs->nlocvars, f->sizelocvars,
                     LocVar, SHRT_MAX, "local variables" );

    while ( oldsize < f->sizelocvars ) {
        f->locvars[oldsize++].varname = NULL;
    }

    f->locvars[fs->nlocvars].varname = varname;
    luaC_objbarrier( ls->L, f, varname );
    return fs->nlocvars++;
}


static void new_localvar( LexState* ls, TString* name ) {
    FuncState* fs = ls->fs;
    Dyndata* dyd = ls->dyd;
    int reg = registerlocalvar( ls, name );
    checklimit( fs, dyd->actvar.n + 1 - fs->firstlocal,
                MAXVARS, "local variables" );
    luaM_growvector( ls->L, dyd->actvar.arr, dyd->actvar.n + 1,
                     dyd->actvar.size, Vardesc, MAX_INT, "local variables" );
    dyd->actvar.arr[dyd->actvar.n++].idx = cast( short, reg );
}


static void new_localvarliteral_( LexState* ls, const char* name, size_t sz ) {
    new_localvar( ls, luaX_newstring( ls, name, sz ) );
}

#define new_localvarliteral(ls,v) \
    new_localvarliteral_(ls, "" v, (sizeof(v)/sizeof(char))-1)


static LocVar* getlocvar( FuncState* fs, int i ) {
    int idx = fs->ls->dyd->actvar.arr[fs->firstlocal + i].idx;
    lua_assert( idx < fs->nlocvars );
    return &fs->f->locvars[idx];
}


static void adjustlocalvars( LexState* ls, int nvars ) {
    FuncState* fs = ls->fs;
    fs->nactvar = cast_byte( fs->nactvar + nvars );

    for ( ; nvars; nvars-- ) {
        getlocvar( fs, fs->nactvar - nvars )->startpc = fs->pc;
    }
}


static void removevars( FuncState* fs, int tolevel ) {
    fs->ls->dyd->actvar.n -= ( fs->nactvar - tolevel );

    while ( fs->nactvar > tolevel ) {
        getlocvar( fs, --fs->nactvar )->endpc = fs->pc;
    }
}


static int searchupvalue( FuncState* fs, TString* name ) {
    int i;
    Upvaldesc* up = fs->f->upvalues;

    for ( i = 0; i < fs->nups; i++ ) {
        if ( eqstr( up[i].name, name ) ) {
            return i;
        }
    }

    return -1;  /* not found */
}


static int newupvalue( FuncState* fs, TString* name, expdesc* v ) {
    Proto* f = fs->f;
    int oldsize = f->sizeupvalues;
    checklimit( fs, fs->nups + 1, MAXUPVAL, "upvalues" );
    luaM_growvector( fs->ls->L, f->upvalues, fs->nups, f->sizeupvalues,
                     Upvaldesc, MAXUPVAL, "upvalues" );

    while ( oldsize < f->sizeupvalues ) {
        f->upvalues[oldsize++].name = NULL;
    }

    f->upvalues[fs->nups].instack = ( v->k == VLOCAL );
    f->upvalues[fs->nups].idx = cast_byte( v->u.info );
    f->upvalues[fs->nups].name = name;
    luaC_objbarrier( fs->ls->L, f, name );
    return fs->nups++;
}


static int searchvar( FuncState* fs, TString* n ) {
    int i;

    for ( i = cast_int( fs->nactvar ) - 1; i >= 0; i-- ) {
        if ( eqstr( n, getlocvar( fs, i )->varname ) ) {
            return i;
        }
    }

    return -1;  /* not found */
}


/*
  Mark block where variable at given level was defined
  (to emit close instructions later).
*/
static void markupval( FuncState* fs, int level ) {
    BlockCnt* bl = fs->bl;

    while ( bl->nactvar > level ) {
        bl = bl->previous;
    }

    bl->upval = 1;
}


/*
  Find variable with given name 'n'. If it is an upvalue, add this
  upvalue into all intermediate functions.
*/
static void singlevaraux( FuncState* fs, TString* n, expdesc* var, int base ) {
    if ( fs == NULL ) { /* no more levels? */
        init_exp( var, VVOID, 0 );    /* default is global */
    }
    else {
        int v = searchvar( fs, n ); /* look up locals at current level */

        if ( v >= 0 ) { /* found? */
            init_exp( var, VLOCAL, v ); /* variable is local */

            if ( !base ) {
                markupval( fs, v );    /* local will be used as an upval */
            }
        }
        else {   /* not found as local at current level; try upvalues */
            int idx = searchupvalue( fs, n ); /* try existing upvalues */

            if ( idx < 0 ) { /* not found? */
                singlevaraux( fs->prev, n, var, 0 ); /* try upper levels */

                if ( var->k == VVOID ) { /* not found? */
                    return;    /* it is a global */
                }

                /* else was LOCAL or UPVAL */
                idx  = newupvalue( fs, n, var ); /* will be a new upvalue */
            }

            init_exp( var, VUPVAL, idx ); /* new or old upvalue */
        }
    }
}


static void singlevar( LexState* ls, expdesc* var ) {
    TString* varname = str_checkname( ls );
    FuncState* fs = ls->fs;
    singlevaraux( fs, varname, var, 1 );

    if ( var->k == VVOID ) { /* global name? */
        expdesc key;
        singlevaraux( fs, ls->envn, var, 1 ); /* get environment variable */
        lua_assert( var->k != VVOID ); /* this one must exist */
        codestring( ls, &key, varname ); /* key is variable name */
        luaK_indexed( fs, var, &key ); /* env[varname] */
    }
}


static void adjust_assign( LexState* ls, int nvars, int nexps, expdesc* e ) {
    FuncState* fs = ls->fs;
    int extra = nvars - nexps;

    if ( hasmultret( e->k ) ) {
        extra++;  /* includes call itself */

        if ( extra < 0 ) {
            extra = 0;
        }

        luaK_setreturns( fs, e, extra ); /* last exp. provides the difference */

        if ( extra > 1 ) {
            luaK_reserveregs( fs, extra - 1 );
        }
    }
    else {
        if ( e->k != VVOID ) {
            luaK_exp2nextreg( fs, e );    /* close last expression */
        }

        if ( extra > 0 ) {
            int reg = fs->freereg;
            luaK_reserveregs( fs, extra );
            luaK_nil( fs, reg, extra );
        }
    }

    if ( nexps > nvars ) {
        ls->fs->freereg -= nexps - nvars;    /* remove extra values */
    }
}


static void enterlevel( LexState* ls ) {
    lua_State* L = ls->L;
    ++L->nCcalls;
    checklimit( ls->fs, L->nCcalls, LUAI_MAXCCALLS, "C levels" );
}


#define leavelevel(ls)  ((ls)->L->nCcalls--)


static void closegoto( LexState* ls, int g, Labeldesc* label ) {
    int i;
    FuncState* fs = ls->fs;
    Labellist* gl = &ls->dyd->gt;
    Labeldesc* gt = &gl->arr[g];
    lua_assert( eqstr( gt->name, label->name ) );

    if ( gt->nactvar < label->nactvar ) {
        TString* vname = getlocvar( fs, gt->nactvar )->varname;
        const char* msg = luaO_pushfstring( ls->L,
                                            "<goto %s> at line %d jumps into the scope of local '%s'",
                                            getstr( gt->name ), gt->line, getstr( vname ) );
        semerror( ls, msg );
    }

    luaK_patchlist( fs, gt->pc, label->pc );

    /* remove goto from pending list */
    for ( i = g; i < gl->n - 1; i++ ) {
        gl->arr[i] = gl->arr[i + 1];
    }

    gl->n--;
}


/*
** try to close a goto with existing labels; this solves backward jumps
*/
static int findlabel( LexState* ls, int g ) {
    int i;
    BlockCnt* bl = ls->fs->bl;
    Dyndata* dyd = ls->dyd;
    Labeldesc* gt = &dyd->gt.arr[g];

    /* check labels in current block for a match */
    for ( i = bl->firstlabel; i < dyd->label.n; i++ ) {
        Labeldesc* lb = &dyd->label.arr[i];

        if ( eqstr( lb->name, gt->name ) ) { /* correct label? */
            if ( gt->nactvar > lb->nactvar &&
                    ( bl->upval || dyd->label.n > bl->firstlabel ) ) {
                luaK_patchclose( ls->fs, gt->pc, lb->nactvar );
            }

            closegoto( ls, g, lb ); /* close it */
            return 1;
        }
    }

    return 0;  /* label not found; cannot close goto */
}


static int newlabelentry( LexState* ls, Labellist* l, TString* name,
                          int line, int pc ) {
    int n = l->n;
    luaM_growvector( ls->L, l->arr, n, l->size,
                     Labeldesc, SHRT_MAX, "labels/gotos" );
    l->arr[n].name = name;
    l->arr[n].line = line;
    l->arr[n].nactvar = ls->fs->nactvar;
    l->arr[n].pc = pc;
    l->n = n + 1;
    return n;
}


/*
** check whether new label 'lb' matches any pending gotos in current
** block; solves forward jumps
*/
static void findgotos( LexState* ls, Labeldesc* lb ) {
    Labellist* gl = &ls->dyd->gt;
    int i = ls->fs->bl->firstgoto;

    while ( i < gl->n ) {
        if ( eqstr( gl->arr[i].name, lb->name ) ) {
            closegoto( ls, i, lb );
        }
        else {
            i++;
        }
    }
}


/*
** export pending gotos to outer level, to check them against
** outer labels; if the block being exited has upvalues, and
** the goto exits the scope of any variable (which can be the
** upvalue), close those variables being exited.
*/
static void movegotosout( FuncState* fs, BlockCnt* bl ) {
    int i = bl->firstgoto;
    Labellist* gl = &fs->ls->dyd->gt;

    /* correct pending gotos to current block and try to close it
       with visible labels */
    while ( i < gl->n ) {
        Labeldesc* gt = &gl->arr[i];

        if ( gt->nactvar > bl->nactvar ) {
            if ( bl->upval ) {
                luaK_patchclose( fs, gt->pc, bl->nactvar );
            }

            gt->nactvar = bl->nactvar;
        }

        if ( !findlabel( fs->ls, i ) ) {
            i++;    /* move to next one */
        }
    }
}


static void enterblock( FuncState* fs, BlockCnt* bl, lu_byte isloop ) {
    bl->isloop = isloop;
    bl->nactvar = fs->nactvar;
    bl->firstlabel = fs->ls->dyd->label.n;
    bl->firstgoto = fs->ls->dyd->gt.n;
    bl->upval = 0;
    bl->previous = fs->bl;
    fs->bl = bl;
    lua_assert( fs->freereg == fs->nactvar );
}


/*
** create a label named 'break' to resolve break statements
*/
static void breaklabel( LexState* ls ) {
    TString* n = luaS_new( ls->L, "break" );
    int l = newlabelentry( ls, &ls->dyd->label, n, 0, ls->fs->pc );
    findgotos( ls, &ls->dyd->label.arr[l] );
}

/*
** generates an error for an undefined 'goto'; choose appropriate
** message when label name is a reserved word (which can only be 'break')
*/
static l_noret undefgoto( LexState* ls, Labeldesc* gt ) {
    const char* msg = isreserved( gt->name )
                      ? "<%s> at line %d not inside a loop"
                      : "no visible label '%s' for <goto> at line %d";
    msg = luaO_pushfstring( ls->L, msg, getstr( gt->name ), gt->line );
    semerror( ls, msg );
}


static void leaveblock( FuncState* fs ) {
    BlockCnt* bl = fs->bl;
    LexState* ls = fs->ls;

    if ( bl->previous && bl->upval ) {
        /* create a 'jump to here' to close upvalues */
        int j = luaK_jump( fs );
        luaK_patchclose( fs, j, bl->nactvar );
        luaK_patchtohere( fs, j );
    }

    if ( bl->isloop ) {
        breaklabel( ls );    /* close pending breaks */
    }

    fs->bl = bl->previous;
    removevars( fs, bl->nactvar );
    lua_assert( bl->nactvar == fs->nactvar );
    fs->freereg = fs->nactvar;  /* free registers */
    ls->dyd->label.n = bl->firstlabel;  /* remove local labels */

    if ( bl->previous ) { /* inner block? */
        movegotosout( fs, bl );    /* update pending gotos to outer block */
    }
    else if ( bl->firstgoto < ls->dyd->gt.n ) { /* pending gotos in outer block? */
        undefgoto( ls, &ls->dyd->gt.arr[bl->firstgoto] );    /* error */
    }
}


/*
** adds a new prototype into list of prototypes
*/
static Proto* addprototype( LexState* ls ) {
    Proto* clp;
    lua_State* L = ls->L;
    FuncState* fs = ls->fs;
    Proto* f = fs->f;  /* prototype of current function */

    if ( fs->np >= f->sizep ) {
        int oldsize = f->sizep;
        luaM_growvector( L, f->p, fs->np, f->sizep, Proto*, MAXARG_Bx, "functions" );

        while ( oldsize < f->sizep ) {
            f->p[oldsize++] = NULL;
        }
    }

    f->p[fs->np++] = clp = luaF_newproto( L );
    luaC_objbarrier( L, f, clp );
    return clp;
}


/*
** codes instruction to create new closure in parent function.
** The OP_CLOSURE instruction must use the last available register,
** so that, if it invokes the GC, the GC knows which registers
** are in use at that time.
*/
static void codeclosure( LexState* ls, expdesc* v ) {
    FuncState* fs = ls->fs->prev;
    init_exp( v, VRELOCABLE, luaK_codeABx( fs, OP_CLOSURE, 0, fs->np - 1 ) );
    luaK_exp2nextreg( fs, v ); /* fix it at the last register */
}


static void open_func( LexState* ls, FuncState* fs, BlockCnt* bl ) {
    Proto* f;
    fs->prev = ls->fs;  /* linked list of funcstates */
    fs->ls = ls;
    ls->fs = fs;
    fs->pc = 0;
    fs->lasttarget = 0;
    fs->jpc = NO_JUMP;
    fs->freereg = 0;
    fs->nk = 0;
    fs->np = 0;
    fs->nups = 0;
    fs->nlocvars = 0;
    fs->nactvar = 0;
    fs->firstlocal = ls->dyd->actvar.n;
    fs->bl = NULL;
    f = fs->f;
    f->source = ls->source;
    f->maxstacksize = 2;  /* registers 0/1 are always valid */
    enterblock( fs, bl, 0 );
}


static void close_func( LexState* ls ) {
    lua_State* L = ls->L;
    FuncState* fs = ls->fs;
    Proto* f = fs->f;
    luaK_ret( fs, 0, 0 ); /* final return */
    leaveblock( fs );
    luaM_reallocvector( L, f->code, f->sizecode, fs->pc, Instruction );
    f->sizecode = fs->pc;
    luaM_reallocvector( L, f->lineinfo, f->sizelineinfo, fs->pc, int );
    f->sizelineinfo = fs->pc;
    luaM_reallocvector( L, f->k, f->sizek, fs->nk, TValue );
    f->sizek = fs->nk;
    luaM_reallocvector( L, f->p, f->sizep, fs->np, Proto* );
    f->sizep = fs->np;
    luaM_reallocvector( L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar );
    f->sizelocvars = fs->nlocvars;
    luaM_reallocvector( L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc );
    f->sizeupvalues = fs->nups;
    lua_assert( fs->bl == NULL );
    ls->fs = fs->prev;
    luaC_checkGC( L );
}



/*============================================================*/
/* GRAMMAR RULES */
/*============================================================*/


/*
** check whether current token is in the follow set of a block.
** 'until' closes syntactical blocks, but do not close scope,
** so it is handled in separate.
*/
static int block_follow( LexState* ls, int withuntil ) {
    switch ( ls->t.token ) {
    case TK_ELSE:
    case TK_ELSEIF:
    case TK_END:
    case TK_EOS:
        return 1;

    case TK_UNTIL:
        return withuntil;

    default:
        return 0;
    }
}


static void statlist( LexState* ls ) {
    /* statlist -> { stat [';'] } */
    while ( !block_follow( ls, 1 ) ) {
        if ( ls->t.token == TK_RETURN ) {
            statement( ls );
            return;  /* 'return' must be last statement */
        }

        statement( ls );
    }
}


static void fieldsel( LexState* ls, expdesc* v ) {
    /* fieldsel -> ['.' | ':'] NAME */
    FuncState* fs = ls->fs;
    expdesc key;
    luaK_exp2anyregup( fs, v );
    luaX_next( ls ); /* skip the dot or colon */
    checkname( ls, &key );
    luaK_indexed( fs, v, &key );
}


static void yindex( LexState* ls, expdesc* v ) {
    /* index -> '[' expr ']' */
    luaX_next( ls ); /* skip the '[' */
    expr( ls, v );
    luaK_exp2val( ls->fs, v );
    checknext( ls, ']' );
}


/*
** {======================================================================
** Rules for Constructors
** =======================================================================
*/


struct ConsControl {
    expdesc v;  /* last list item read */
    expdesc* t;  /* table descriptor */
    int nh;  /* total number of 'record' elements */
    int na;  /* total number of array elements */
    int tostore;  /* number of array elements pending to be stored */
};


static void recfield( LexState* ls, struct ConsControl* cc ) {
    /* recfield -> (NAME | '['exp1']') = exp1 */
    FuncState* fs = ls->fs;
    int reg = ls->fs->freereg;
    expdesc key, val;
    int rkkey;

    if ( ls->t.token == TK_NAME ) {
        checklimit( fs, cc->nh, MAX_INT, "items in a constructor" );
        checkname( ls, &key );
    }
    else { /* ls->t.token == '[' */
        yindex( ls, &key );
    }

    cc->nh++;
    checknext( ls, '=' );
    rkkey = luaK_exp2RK( fs, &key );
    expr( ls, &val );
    luaK_codeABC( fs, OP_SETTABLE, cc->t->u.info, rkkey, luaK_exp2RK( fs, &val ) );
    fs->freereg = reg;  /* free registers */
}


static void closelistfield( FuncState* fs, struct ConsControl* cc ) {
    if ( cc->v.k == VVOID ) {
        return;    /* there is no list item */
    }

    luaK_exp2nextreg( fs, &cc->v );
    cc->v.k = VVOID;

    if ( cc->tostore == LFIELDS_PER_FLUSH ) {
        luaK_setlist( fs, cc->t->u.info, cc->na, cc->tostore ); /* flush */
        cc->tostore = 0;  /* no more items pending */
    }
}


static void lastlistfield( FuncState* fs, struct ConsControl* cc ) {
    if ( cc->tostore == 0 ) {
        return;
    }

    if ( hasmultret( cc->v.k ) ) {
        luaK_setmultret( fs, &cc->v );
        luaK_setlist( fs, cc->t->u.info, cc->na, LUA_MULTRET );
        cc->na--;  /* do not count last expression (unknown number of elements) */
    }
    else {
        if ( cc->v.k != VVOID ) {
            luaK_exp2nextreg( fs, &cc->v );
        }

        luaK_setlist( fs, cc->t->u.info, cc->na, cc->tostore );
    }
}


static void listfield( LexState* ls, struct ConsControl* cc ) {
    /* listfield -> exp */
    expr( ls, &cc->v );
    checklimit( ls->fs, cc->na, MAX_INT, "items in a constructor" );
    cc->na++;
    cc->tostore++;
}


static void field( LexState* ls, struct ConsControl* cc ) {
    /* field -> listfield | recfield */
    switch ( ls->t.token ) {
    case TK_NAME: {  /* may be 'listfield' or 'recfield' */
        if ( luaX_lookahead( ls ) != '=' ) { /* expression? */
            listfield( ls, cc );
        }
        else {
            recfield( ls, cc );
        }

        break;
    }

    case '[': {
        recfield( ls, cc );
        break;
    }

    default: {
        listfield( ls, cc );
        break;
    }
    }
}


static void constructor( LexState* ls, expdesc* t ) {
    /* constructor -> '{' [ field { sep field } [sep] ] '}'
       sep -> ',' | ';' */
    FuncState* fs = ls->fs;
    int line = ls->linenumber;
    int pc = luaK_codeABC( fs, OP_NEWTABLE, 0, 0, 0 );
    struct ConsControl cc;
    cc.na = cc.nh = cc.tostore = 0;
    cc.t = t;
    init_exp( t, VRELOCABLE, pc );
    init_exp( &cc.v, VVOID, 0 ); /* no value (yet) */
    luaK_exp2nextreg( ls->fs, t ); /* fix it at stack top */
    checknext( ls, '{' );

    do {
        lua_assert( cc.v.k == VVOID || cc.tostore > 0 );

        if ( ls->t.token == '}' ) {
            break;
        }

        closelistfield( fs, &cc );
        field( ls, &cc );
    }
    while ( testnext( ls, ',' ) || testnext( ls, ';' ) );

    check_match( ls, '}', '{', line );
    lastlistfield( fs, &cc );
    SETARG_B( fs->f->code[pc], luaO_int2fb( cc.na ) ); /* set initial array size */
    SETARG_C( fs->f->code[pc], luaO_int2fb( cc.nh ) ); /* set initial table size */
}

/* }====================================================================== */



static void parlist( LexState* ls ) {
    /* parlist -> [ param { ',' param } ] */
    FuncState* fs = ls->fs;
    Proto* f = fs->f;
    int nparams = 0;
    f->is_vararg = 0;

    if ( ls->t.token != ')' ) { /* is 'parlist' not empty? */
        do {
            switch ( ls->t.token ) {
            case TK_NAME: {  /* param -> NAME */
                new_localvar( ls, str_checkname( ls ) );
                nparams++;
                break;
            }

            case TK_DOTS: {  /* param -> '...' */
                luaX_next( ls );
                f->is_vararg = 1;  /* declared vararg */
                break;
            }

            default:
                luaX_syntaxerror( ls, "<name> or '...' expected" );
            }
        }
        while ( !f->is_vararg && testnext( ls, ',' ) );
    }

    adjustlocalvars( ls, nparams );
    f->numparams = cast_byte( fs->nactvar );
    luaK_reserveregs( fs, fs->nactvar ); /* reserve register for parameters */
}


static void body( LexState* ls, expdesc* e, int ismethod, int line ) {
    /* body ->  '(' parlist ')' block END */
    FuncState new_fs;
    BlockCnt bl;
    new_fs.f = addprototype( ls );
    new_fs.f->linedefined = line;
    open_func( ls, &new_fs, &bl );
    checknext( ls, '(' );

    if ( ismethod ) {
        new_localvarliteral( ls, "self" ); /* create 'self' parameter */
        adjustlocalvars( ls, 1 );
    }

    parlist( ls );
    checknext( ls, ')' );
    statlist( ls );
    new_fs.f->lastlinedefined = ls->linenumber;
    check_match( ls, TK_END, TK_FUNCTION, line );
    codeclosure( ls, e );
    close_func( ls );
}


static int explist( LexState* ls, expdesc* v ) {
    /* explist -> expr { ',' expr } */
    int n = 1;  /* at least one expression */
    expr( ls, v );

    while ( testnext( ls, ',' ) ) {
        luaK_exp2nextreg( ls->fs, v );
        expr( ls, v );
        n++;
    }

    return n;
}


static void funcargs( LexState* ls, expdesc* f, int line ) {
    FuncState* fs = ls->fs;
    expdesc args;
    int base, nparams;

    switch ( ls->t.token ) {
    case '(': {  /* funcargs -> '(' [ explist ] ')' */
        luaX_next( ls );

        if ( ls->t.token == ')' ) { /* arg list is empty? */
            args.k = VVOID;
        }
        else {
            explist( ls, &args );
            luaK_setmultret( fs, &args );
        }

        check_match( ls, ')', '(', line );
        break;
    }

    case '{': {  /* funcargs -> constructor */
        constructor( ls, &args );
        break;
    }

    case TK_STRING: {  /* funcargs -> STRING */
        codestring( ls, &args, ls->t.seminfo.ts );
        luaX_next( ls ); /* must use 'seminfo' before 'next' */
        break;
    }

    default: {
        luaX_syntaxerror( ls, "function arguments expected" );
    }
    }

    lua_assert( f->k == VNONRELOC );
    base = f->u.info;  /* base register for call */

    if ( hasmultret( args.k ) ) {
        nparams = LUA_MULTRET;    /* open call */
    }
    else {
        if ( args.k != VVOID ) {
            luaK_exp2nextreg( fs, &args );    /* close last argument */
        }

        nparams = fs->freereg - ( base + 1 );
    }

    init_exp( f, VCALL, luaK_codeABC( fs, OP_CALL, base, nparams + 1, 2 ) );
    luaK_fixline( fs, line );
    fs->freereg = base + 1;  /* call remove function and arguments and leaves
                            (unless changed) one result */
}




/*
** {======================================================================
** Expression parsing
** =======================================================================
*/


static void primaryexp( LexState* ls, expdesc* v ) {
    /* primaryexp -> NAME | '(' expr ')' */
    switch ( ls->t.token ) {
    case '(': {
        int line = ls->linenumber;
        luaX_next( ls );
        expr( ls, v );
        check_match( ls, ')', '(', line );
        luaK_dischargevars( ls->fs, v );
        return;
    }

    case TK_NAME: {
        singlevar( ls, v );
        return;
    }

    default: {
        luaX_syntaxerror( ls, "unexpected symbol" );
    }
    }
}


static void suffixedexp( LexState* ls, expdesc* v ) {
    /* suffixedexp ->
         primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
    FuncState* fs = ls->fs;
    int line = ls->linenumber;
    primaryexp( ls, v );

    for ( ;; ) {
        switch ( ls->t.token ) {
        case '.': {  /* fieldsel */
            fieldsel( ls, v );
            break;
        }

        case '[': {  /* '[' exp1 ']' */
            expdesc key;
            luaK_exp2anyregup( fs, v );
            yindex( ls, &key );
            luaK_indexed( fs, v, &key );
            break;
        }

        case ':': {  /* ':' NAME funcargs */
            expdesc key;
            luaX_next( ls );
            checkname( ls, &key );
            luaK_self( fs, v, &key );
            funcargs( ls, v, line );
            break;
        }

        case '(':
        case TK_STRING:
        case '{': {  /* funcargs */
            luaK_exp2nextreg( fs, v );
            funcargs( ls, v, line );
            break;
        }

        default:
            return;
        }
    }
}


static void simpleexp( LexState* ls, expdesc* v ) {
    /* simpleexp -> FLT | INT | STRING | NIL | TRUE | FALSE | ... |
                    constructor | FUNCTION body | suffixedexp */
    switch ( ls->t.token ) {
    case TK_FLT: {
        init_exp( v, VKFLT, 0 );
        v->u.nval = ls->t.seminfo.r;
        break;
    }

    case TK_INT: {
        init_exp( v, VKINT, 0 );
        v->u.ival = ls->t.seminfo.i;
        break;
    }

    case TK_STRING: {
        codestring( ls, v, ls->t.seminfo.ts );
        break;
    }

    case TK_NIL: {
        init_exp( v, VNIL, 0 );
        break;
    }

    case TK_TRUE: {
        init_exp( v, VTRUE, 0 );
        break;
    }

    case TK_FALSE: {
        init_exp( v, VFALSE, 0 );
        break;
    }

    case TK_DOTS: {  /* vararg */
        FuncState* fs = ls->fs;
        check_condition( ls, fs->f->is_vararg,
                         "cannot use '...' outside a vararg function" );
        init_exp( v, VVARARG, luaK_codeABC( fs, OP_VARARG, 0, 1, 0 ) );
        break;
    }

    case '{': {  /* constructor */
        constructor( ls, v );
        return;
    }

    case TK_FUNCTION: {
        luaX_next( ls );
        body( ls, v, 0, ls->linenumber );
        return;
    }

    default: {
        suffixedexp( ls, v );
        return;
    }
    }

    luaX_next( ls );
}


static UnOpr getunopr( int op ) {
    switch ( op ) {
    case TK_NOT:
        return OPR_NOT;

    case '-':
        return OPR_MINUS;

    case '~':
        return OPR_BNOT;

    case '#':
        return OPR_LEN;

    default:
        return OPR_NOUNOPR;
    }
}


static BinOpr getbinopr( int op ) {
    switch ( op ) {
    case '+':
        return OPR_ADD;

    case '-':
        return OPR_SUB;

    case '*':
        return OPR_MUL;

    case '%':
        return OPR_MOD;

    case '^':
        return OPR_POW;

    case '/':
        return OPR_DIV;

    case TK_IDIV:
        return OPR_IDIV;

    case '&':
        return OPR_BAND;

    case '|':
        return OPR_BOR;

    case '~':
        return OPR_BXOR;

    case TK_SHL:
        return OPR_SHL;

    case TK_SHR:
        return OPR_SHR;

    case TK_CONCAT:
        return OPR_CONCAT;

    case TK_NE:
        return OPR_NE;

    case TK_EQ:
        return OPR_EQ;

    case '<':
        return OPR_LT;

    case TK_LE:
        return OPR_LE;

    case '>':
        return OPR_GT;

    case TK_GE:
        return OPR_GE;

    case TK_AND:
        return OPR_AND;

    case TK_OR:
        return OPR_OR;

    default:
        return OPR_NOBINOPR;
    }
}


static const struct {
    lu_byte left;  /* left priority for each binary operator */
    lu_byte right; /* right priority */
} priority[] =    /* ORDER OPR */
{
    {10, 10}, {10, 10},           /* '+' '-' */
    {11, 11}, {11, 11},           /* '*' '%' */
    {14, 13},                  /* '^' (right associative) */
    {11, 11}, {11, 11},           /* '/' '//' */
    {6, 6}, {4, 4}, {5, 5},   /* '&' '|' '~' */
    {7, 7}, {7, 7},           /* '<<' '>>' */
    {9, 8},                   /* '..' (right associative) */
    {3, 3}, {3, 3}, {3, 3},   /* ==, <, <= */
    {3, 3}, {3, 3}, {3, 3},   /* ~=, >, >= */
    {2, 2}, {1, 1}            /* and, or */
};

#define UNARY_PRIORITY  12  /* priority for unary operators */


/*
** subexpr -> (simpleexp | unop subexpr) { binop subexpr }
** where 'binop' is any binary operator with a priority higher than 'limit'
*/
static BinOpr subexpr( LexState* ls, expdesc* v, int limit ) {
    BinOpr op;
    UnOpr uop;
    enterlevel( ls );
    uop = getunopr( ls->t.token );

    if ( uop != OPR_NOUNOPR ) {
        int line = ls->linenumber;
        luaX_next( ls );
        subexpr( ls, v, UNARY_PRIORITY );
        luaK_prefix( ls->fs, uop, v, line );
    }
    else {
        simpleexp( ls, v );
    }

    /* expand while operators have priorities higher than 'limit' */
    op = getbinopr( ls->t.token );

    while ( op != OPR_NOBINOPR && priority[op].left > limit ) {
        expdesc v2;
        BinOpr nextop;
        int line = ls->linenumber;
        luaX_next( ls );
        luaK_infix( ls->fs, op, v );
        /* read sub-expression with higher priority */
        nextop = subexpr( ls, &v2, priority[op].right );
        luaK_posfix( ls->fs, op, v, &v2, line );
        op = nextop;
    }

    leavelevel( ls );
    return op;  /* return first untreated operator */
}


static void expr( LexState* ls, expdesc* v ) {
    subexpr( ls, v, 0 );
}

/* }==================================================================== */



/*
** {======================================================================
** Rules for Statements
** =======================================================================
*/


static void block( LexState* ls ) {
    /* block -> statlist */
    FuncState* fs = ls->fs;
    BlockCnt bl;
    enterblock( fs, &bl, 0 );
    statlist( ls );
    leaveblock( fs );
}


/*
** structure to chain all variables in the left-hand side of an
** assignment
*/
struct LHS_assign {
    struct LHS_assign* prev;
    expdesc v;  /* variable (global, local, upvalue, or indexed) */
};


/*
** check whether, in an assignment to an upvalue/local variable, the
** upvalue/local variable is begin used in a previous assignment to a
** table. If so, save original upvalue/local value in a safe place and
** use this safe copy in the previous assignment.
*/
static void check_conflict( LexState* ls, struct LHS_assign* lh, expdesc* v ) {
    FuncState* fs = ls->fs;
    int extra = fs->freereg;  /* eventual position to save local variable */
    int conflict = 0;

    for ( ; lh; lh = lh->prev ) { /* check all previous assignments */
        if ( lh->v.k == VINDEXED ) { /* assigning to a table? */
            /* table is the upvalue/local being assigned now? */
            if ( lh->v.u.ind.vt == v->k && lh->v.u.ind.t == v->u.info ) {
                conflict = 1;
                lh->v.u.ind.vt = VLOCAL;
                lh->v.u.ind.t = extra;  /* previous assignment will use safe copy */
            }

            /* index is the local being assigned? (index cannot be upvalue) */
            if ( v->k == VLOCAL && lh->v.u.ind.idx == v->u.info ) {
                conflict = 1;
                lh->v.u.ind.idx = extra;  /* previous assignment will use safe copy */
            }
        }
    }

    if ( conflict ) {
        /* copy upvalue/local value to a temporary (in position 'extra') */
        OpCode op = ( v->k == VLOCAL ) ? OP_MOVE : OP_GETUPVAL;
        luaK_codeABC( fs, op, extra, v->u.info, 0 );
        luaK_reserveregs( fs, 1 );
    }
}


static void assignment( LexState* ls, struct LHS_assign* lh, int nvars ) {
    expdesc e;
    check_condition( ls, vkisvar( lh->v.k ), "syntax error" );

    if ( testnext( ls, ',' ) ) { /* assignment -> ',' suffixedexp assignment */
        struct LHS_assign nv;
        nv.prev = lh;
        suffixedexp( ls, &nv.v );

        if ( nv.v.k != VINDEXED ) {
            check_conflict( ls, lh, &nv.v );
        }

        checklimit( ls->fs, nvars + ls->L->nCcalls, LUAI_MAXCCALLS,
                    "C levels" );
        assignment( ls, &nv, nvars + 1 );
    }
    else {   /* assignment -> '=' explist */
        int nexps;
        checknext( ls, '=' );
        nexps = explist( ls, &e );

        if ( nexps != nvars ) {
            adjust_assign( ls, nvars, nexps, &e );
        }
        else {
            luaK_setoneret( ls->fs, &e ); /* close last expression */
            luaK_storevar( ls->fs, &lh->v, &e );
            return;  /* avoid default */
        }
    }

    init_exp( &e, VNONRELOC, ls->fs->freereg - 1 ); /* default assignment */
    luaK_storevar( ls->fs, &lh->v, &e );
}


static int cond( LexState* ls ) {
    /* cond -> exp */
    expdesc v;
    expr( ls, &v ); /* read condition */

    if ( v.k == VNIL ) {
        v.k = VFALSE;    /* 'falses' are all equal here */
    }

    luaK_goiftrue( ls->fs, &v );
    return v.f;
}


static void gotostat( LexState* ls, int pc ) {
    int line = ls->linenumber;
    TString* label;
    int g;

    if ( testnext( ls, TK_GOTO ) ) {
        label = str_checkname( ls );
    }
    else {
        luaX_next( ls ); /* skip break */
        label = luaS_new( ls->L, "break" );
    }

    g = newlabelentry( ls, &ls->dyd->gt, label, line, pc );
    findlabel( ls, g ); /* close it if label already defined */
}


/* check for repeated labels on the same block */
static void checkrepeated( FuncState* fs, Labellist* ll, TString* label ) {
    int i;

    for ( i = fs->bl->firstlabel; i < ll->n; i++ ) {
        if ( eqstr( label, ll->arr[i].name ) ) {
            const char* msg = luaO_pushfstring( fs->ls->L,
                                                "label '%s' already defined on line %d",
                                                getstr( label ), ll->arr[i].line );
            semerror( fs->ls, msg );
        }
    }
}


/* skip no-op statements */
static void skipnoopstat( LexState* ls ) {
    while ( ls->t.token == ';' || ls->t.token == TK_DBCOLON ) {
        statement( ls );
    }
}


static void labelstat( LexState* ls, TString* label, int line ) {
    /* label -> '::' NAME '::' */
    FuncState* fs = ls->fs;
    Labellist* ll = &ls->dyd->label;
    int l;  /* index of new label being created */
    checkrepeated( fs, ll, label ); /* check for repeated labels */
    checknext( ls, TK_DBCOLON ); /* skip double colon */
    /* create new entry for this label */
    l = newlabelentry( ls, ll, label, line, luaK_getlabel( fs ) );
    skipnoopstat( ls ); /* skip other no-op statements */

    if ( block_follow( ls, 0 ) ) { /* label is last no-op statement in the block? */
        /* assume that locals are already out of scope */
        ll->arr[l].nactvar = fs->bl->nactvar;
    }

    findgotos( ls, &ll->arr[l] );
}


static void whilestat( LexState* ls, int line ) {
    /* whilestat -> WHILE cond DO block END */
    FuncState* fs = ls->fs;
    int whileinit;
    int condexit;
    BlockCnt bl;
    luaX_next( ls ); /* skip WHILE */
    whileinit = luaK_getlabel( fs );
    condexit = cond( ls );
    enterblock( fs, &bl, 1 );
    checknext( ls, TK_DO );
    block( ls );
    luaK_jumpto( fs, whileinit );
    check_match( ls, TK_END, TK_WHILE, line );
    leaveblock( fs );
    luaK_patchtohere( fs, condexit ); /* false conditions finish the loop */
}


static void repeatstat( LexState* ls, int line ) {
    /* repeatstat -> REPEAT block UNTIL cond */
    int condexit;
    FuncState* fs = ls->fs;
    int repeat_init = luaK_getlabel( fs );
    BlockCnt bl1, bl2;
    enterblock( fs, &bl1, 1 ); /* loop block */
    enterblock( fs, &bl2, 0 ); /* scope block */
    luaX_next( ls ); /* skip REPEAT */
    statlist( ls );
    check_match( ls, TK_UNTIL, TK_REPEAT, line );
    condexit = cond( ls ); /* read condition (inside scope block) */

    if ( bl2.upval ) { /* upvalues? */
        luaK_patchclose( fs, condexit, bl2.nactvar );
    }

    leaveblock( fs ); /* finish scope */
    luaK_patchlist( fs, condexit, repeat_init ); /* close the loop */
    leaveblock( fs ); /* finish loop */
}


static int exp1( LexState* ls ) {
    expdesc e;
    int reg;
    expr( ls, &e );
    luaK_exp2nextreg( ls->fs, &e );
    lua_assert( e.k == VNONRELOC );
    reg = e.u.info;
    return reg;
}


static void forbody( LexState* ls, int base, int line, int nvars, int isnum ) {
    /* forbody -> DO block */
    BlockCnt bl;
    FuncState* fs = ls->fs;
    int prep, endfor;
    adjustlocalvars( ls, 3 ); /* control variables */
    checknext( ls, TK_DO );
    prep = isnum ? luaK_codeAsBx( fs, OP_FORPREP, base, NO_JUMP ) : luaK_jump( fs );
    enterblock( fs, &bl, 0 ); /* scope for declared variables */
    adjustlocalvars( ls, nvars );
    luaK_reserveregs( fs, nvars );
    block( ls );
    leaveblock( fs ); /* end of scope for declared variables */
    luaK_patchtohere( fs, prep );

    if ( isnum ) { /* numeric for? */
        endfor = luaK_codeAsBx( fs, OP_FORLOOP, base, NO_JUMP );
    }
    else {  /* generic for */
        luaK_codeABC( fs, OP_TFORCALL, base, 0, nvars );
        luaK_fixline( fs, line );
        endfor = luaK_codeAsBx( fs, OP_TFORLOOP, base + 2, NO_JUMP );
    }

    luaK_patchlist( fs, endfor, prep + 1 );
    luaK_fixline( fs, line );
}


static void fornum( LexState* ls, TString* varname, int line ) {
    /* fornum -> NAME = exp1,exp1[,exp1] forbody */
    FuncState* fs = ls->fs;
    int base = fs->freereg;
    new_localvarliteral( ls, "(for index)" );
    new_localvarliteral( ls, "(for limit)" );
    new_localvarliteral( ls, "(for step)" );
    new_localvar( ls, varname );
    checknext( ls, '=' );
    exp1( ls ); /* initial value */
    checknext( ls, ',' );
    exp1( ls ); /* limit */

    if ( testnext( ls, ',' ) ) {
        exp1( ls );    /* optional step */
    }
    else {  /* default step = 1 */
        luaK_codek( fs, fs->freereg, luaK_intK( fs, 1 ) );
        luaK_reserveregs( fs, 1 );
    }

    forbody( ls, base, line, 1, 1 );
}


static void forlist( LexState* ls, TString* indexname ) {
    /* forlist -> NAME {,NAME} IN explist forbody */
    FuncState* fs = ls->fs;
    expdesc e;
    int nvars = 4;  /* gen, state, control, plus at least one declared var */
    int line;
    int base = fs->freereg;
    /* create control variables */
    new_localvarliteral( ls, "(for generator)" );
    new_localvarliteral( ls, "(for state)" );
    new_localvarliteral( ls, "(for control)" );
    /* create declared variables */
    new_localvar( ls, indexname );

    while ( testnext( ls, ',' ) ) {
        new_localvar( ls, str_checkname( ls ) );
        nvars++;
    }

    checknext( ls, TK_IN );
    line = ls->linenumber;
    adjust_assign( ls, 3, explist( ls, &e ), &e );
    luaK_checkstack( fs, 3 ); /* extra space to call generator */
    forbody( ls, base, line, nvars - 3, 0 );
}


static void forstat( LexState* ls, int line ) {
    /* forstat -> FOR (fornum | forlist) END */
    FuncState* fs = ls->fs;
    TString* varname;
    BlockCnt bl;
    enterblock( fs, &bl, 1 ); /* scope for loop and control variables */
    luaX_next( ls ); /* skip 'for' */
    varname = str_checkname( ls ); /* first variable name */

    switch ( ls->t.token ) {
    case '=':
        fornum( ls, varname, line );
        break;

    case ',':
    case TK_IN:
        forlist( ls, varname );
        break;

    default:
        luaX_syntaxerror( ls, "'=' or 'in' expected" );
    }

    check_match( ls, TK_END, TK_FOR, line );
    leaveblock( fs ); /* loop scope ('break' jumps to this point) */
}


static void test_then_block( LexState* ls, int* escapelist ) {
    /* test_then_block -> [IF | ELSEIF] cond THEN block */
    BlockCnt bl;
    FuncState* fs = ls->fs;
    expdesc v;
    int jf;  /* instruction to skip 'then' code (if condition is false) */
    luaX_next( ls ); /* skip IF or ELSEIF */
    expr( ls, &v ); /* read condition */
    checknext( ls, TK_THEN );

    if ( ls->t.token == TK_GOTO || ls->t.token == TK_BREAK ) {
        luaK_goiffalse( ls->fs, &v ); /* will jump to label if condition is true */
        enterblock( fs, &bl, 0 ); /* must enter block before 'goto' */
        gotostat( ls, v.t ); /* handle goto/break */
        skipnoopstat( ls ); /* skip other no-op statements */

        if ( block_follow( ls, 0 ) ) { /* 'goto' is the entire block? */
            leaveblock( fs );
            return;  /* and that is it */
        }
        else { /* must skip over 'then' part if condition is false */
            jf = luaK_jump( fs );
        }
    }
    else {   /* regular case (not goto/break) */
        luaK_goiftrue( ls->fs, &v ); /* skip over block if condition is false */
        enterblock( fs, &bl, 0 );
        jf = v.f;
    }

    statlist( ls ); /* 'then' part */
    leaveblock( fs );

    if ( ls->t.token == TK_ELSE ||
            ls->t.token == TK_ELSEIF ) { /* followed by 'else'/'elseif'? */
        luaK_concat( fs, escapelist, luaK_jump( fs ) );    /* must jump over it */
    }

    luaK_patchtohere( fs, jf );
}


static void ifstat( LexState* ls, int line ) {
    /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
    FuncState* fs = ls->fs;
    int escapelist = NO_JUMP;  /* exit list for finished parts */
    test_then_block( ls, &escapelist ); /* IF cond THEN block */

    while ( ls->t.token == TK_ELSEIF ) {
        test_then_block( ls, &escapelist );    /* ELSEIF cond THEN block */
    }

    if ( testnext( ls, TK_ELSE ) ) {
        block( ls );    /* 'else' part */
    }

    check_match( ls, TK_END, TK_IF, line );
    luaK_patchtohere( fs, escapelist ); /* patch escape list to 'if' end */
}


static void localfunc( LexState* ls ) {
    expdesc b;
    FuncState* fs = ls->fs;
    new_localvar( ls, str_checkname( ls ) ); /* new local variable */
    adjustlocalvars( ls, 1 ); /* enter its scope */
    body( ls, &b, 0, ls->linenumber ); /* function created in next register */
    /* debug information will only see the variable after this point! */
    getlocvar( fs, b.u.info )->startpc = fs->pc;
}


static void localstat( LexState* ls ) {
    /* stat -> LOCAL NAME {',' NAME} ['=' explist] */
    int nvars = 0;
    int nexps;
    expdesc e;

    do {
        new_localvar( ls, str_checkname( ls ) );
        nvars++;
    }
    while ( testnext( ls, ',' ) );

    if ( testnext( ls, '=' ) ) {
        nexps = explist( ls, &e );
    }
    else {
        e.k = VVOID;
        nexps = 0;
    }

    adjust_assign( ls, nvars, nexps, &e );
    adjustlocalvars( ls, nvars );
}


static int funcname( LexState* ls, expdesc* v ) {
    /* funcname -> NAME {fieldsel} [':' NAME] */
    int ismethod = 0;
    singlevar( ls, v );

    while ( ls->t.token == '.' ) {
        fieldsel( ls, v );
    }

    if ( ls->t.token == ':' ) {
        ismethod = 1;
        fieldsel( ls, v );
    }

    return ismethod;
}


static void funcstat( LexState* ls, int line ) {
    /* funcstat -> FUNCTION funcname body */
    int ismethod;
    expdesc v, b;
    luaX_next( ls ); /* skip FUNCTION */
    ismethod = funcname( ls, &v );
    body( ls, &b, ismethod, line );
    luaK_storevar( ls->fs, &v, &b );
    luaK_fixline( ls->fs, line ); /* definition "happens" in the first line */
}


static void exprstat( LexState* ls ) {
    /* stat -> func | assignment */
    FuncState* fs = ls->fs;
    struct LHS_assign v;
    suffixedexp( ls, &v.v );

    if ( ls->t.token == '=' || ls->t.token == ',' ) { /* stat -> assignment ? */
        v.prev = NULL;
        assignment( ls, &v, 1 );
    }
    else {   /* stat -> func */
        check_condition( ls, v.v.k == VCALL, "syntax error" );
        SETARG_C( getinstruction( fs, &v.v ), 1 ); /* call statement uses no results */
    }
}


static void retstat( LexState* ls ) {
    /* stat -> RETURN [explist] [';'] */
    FuncState* fs = ls->fs;
    expdesc e;
    int first, nret;  /* registers with returned values */

    if ( block_follow( ls, 1 ) || ls->t.token == ';' ) {
        first = nret = 0;    /* return no values */
    }
    else {
        nret = explist( ls, &e ); /* optional return values */

        if ( hasmultret( e.k ) ) {
            luaK_setmultret( fs, &e );

            if ( e.k == VCALL && nret == 1 ) { /* tail call? */
                SET_OPCODE( getinstruction( fs, &e ), OP_TAILCALL );
                lua_assert( GETARG_A( getinstruction( fs, &e ) ) == fs->nactvar );
            }

            first = fs->nactvar;
            nret = LUA_MULTRET;  /* return all values */
        }
        else {
            if ( nret == 1 ) { /* only one single value? */
                first = luaK_exp2anyreg( fs, &e );
            }
            else {
                luaK_exp2nextreg( fs, &e ); /* values must go to the stack */
                first = fs->nactvar;  /* return all active values */
                lua_assert( nret == fs->freereg - first );
            }
        }
    }

    luaK_ret( fs, first, nret );
    testnext( ls, ';' ); /* skip optional semicolon */
}


static void statement( LexState* ls ) {
    int line = ls->linenumber;  /* may be needed for error messages */
    enterlevel( ls );

    switch ( ls->t.token ) {
    case ';': {  /* stat -> ';' (empty statement) */
        luaX_next( ls ); /* skip ';' */
        break;
    }

    case TK_IF: {  /* stat -> ifstat */
        ifstat( ls, line );
        break;
    }

    case TK_WHILE: {  /* stat -> whilestat */
        whilestat( ls, line );
        break;
    }

    case TK_DO: {  /* stat -> DO block END */
        luaX_next( ls ); /* skip DO */
        block( ls );
        check_match( ls, TK_END, TK_DO, line );
        break;
    }

    case TK_FOR: {  /* stat -> forstat */
        forstat( ls, line );
        break;
    }

    case TK_REPEAT: {  /* stat -> repeatstat */
        repeatstat( ls, line );
        break;
    }

    case TK_FUNCTION: {  /* stat -> funcstat */
        funcstat( ls, line );
        break;
    }

    case TK_LOCAL: {  /* stat -> localstat */
        luaX_next( ls ); /* skip LOCAL */

        if ( testnext( ls, TK_FUNCTION ) ) { /* local function? */
            localfunc( ls );
        }
        else {
            localstat( ls );
        }

        break;
    }

    case TK_DBCOLON: {  /* stat -> label */
        luaX_next( ls ); /* skip double colon */
        labelstat( ls, str_checkname( ls ), line );
        break;
    }

    case TK_RETURN: {  /* stat -> retstat */
        luaX_next( ls ); /* skip RETURN */
        retstat( ls );
        break;
    }

    case TK_BREAK:   /* stat -> breakstat */
    case TK_GOTO: {  /* stat -> 'goto' NAME */
        gotostat( ls, luaK_jump( ls->fs ) );
        break;
    }

    default: {  /* stat -> func | assignment */
        exprstat( ls );
        break;
    }
    }

    lua_assert( ls->fs->f->maxstacksize >= ls->fs->freereg &&
                ls->fs->freereg >= ls->fs->nactvar );
    ls->fs->freereg = ls->fs->nactvar;  /* free registers */
    leavelevel( ls );
}

/* }====================================================================== */


/*
** compiles the main function, which is a regular vararg function with an
** upvalue named LUA_ENV
*/
static void mainfunc( LexState* ls, FuncState* fs ) {
    BlockCnt bl;
    expdesc v;
    open_func( ls, fs, &bl );
    fs->f->is_vararg = 1;  /* main function is always declared vararg */
    init_exp( &v, VLOCAL, 0 ); /* create and... */
    newupvalue( fs, ls->envn, &v ); /* ...set environment upvalue */
    luaX_next( ls ); /* read first token */
    statlist( ls ); /* parse main body */
    check( ls, TK_EOS );
    close_func( ls );
}


LClosure* luaY_parser( lua_State* L, ZIO* z, Mbuffer* buff,
                       Dyndata* dyd, const char* name, int firstchar ) {
    LexState lexstate;
    FuncState funcstate;
    LClosure* cl = luaF_newLclosure( L, 1 ); /* create main closure */
    setclLvalue( L, L->top, cl ); /* anchor it (to avoid being collected) */
    luaD_inctop( L );
    lexstate.h = luaH_new( L ); /* create table for scanner */
    sethvalue( L, L->top, lexstate.h ); /* anchor it */
    luaD_inctop( L );
    funcstate.f = cl->p = luaF_newproto( L );
    funcstate.f->source = luaS_new( L, name ); /* create and anchor TString */
    lua_assert( iswhite( funcstate.f ) ); /* do not need barrier here */
    lexstate.buff = buff;
    lexstate.dyd = dyd;
    dyd->actvar.n = dyd->gt.n = dyd->label.n = 0;
    luaX_setinput( L, &lexstate, z, funcstate.f->source, firstchar );
    mainfunc( &lexstate, &funcstate );
    lua_assert( !funcstate.prev && funcstate.nups == 1 && !lexstate.fs );
    /* all scopes should be correctly finished */
    lua_assert( dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0 );
    L->top--;  /* remove scanner's table */
    return cl;  /* closure is on the stack, too */
}

